﻿Imports Microsoft.VisualBasic
Imports System.Security.Cryptography
Imports System.Net

Namespace WebApi


    Namespace Twitter


        Public Class TwitterClient
            'https://dev.twitter.com/docs/api/1.1 to see all twitter rest api functions

            Private webClient As WebApi.Net.WebClient

            Public Property LastError As Net.ServiceError

            Public Property AutoProc As MyApp.Task.TaskType

            Public Sub New(oauth As WebApi.Oauth.Oauth)
                'oauth.oauthConsumerSecret = "kLgJlMmE0iWToUUK3xF15K8f9dwYjYjJRHg3o7Zw"
                'oauth.oauthConsumerKey = "N89RIjsDiPGuqzHDMteSQ"

                Me.webClient = New WebApi.Net.WebClient
                Me.webClient.oAuth = oauth
                'Me.webClient.oAuth.oauthMethod = "GET"


            End Sub

            Dim Res As IO.StreamReader
            Private Sub DownloadInit(address As String)

                If Res Is Nothing Then
                    Res = New IO.StreamReader(Me.webClient.OpenRead(address), Encoding.UTF8)

                End If

            End Sub
            Private Function DownloadLine() As String

                Return Res.ReadLine

            End Function

            Private Function Download(address As String) As String

                Me.LastError = New Net.ServiceError With {
                    .webClient = Me.webClient,
                    .Net = WebApi.Type.TWITTER,
                    .AutoProc = Me.AutoProc
                }

                Dim Res = ""
                Dim TryDonwload = 0
                Do
                    Try
                        Res = Encoding.UTF8.GetString(Me.webClient.DownloadData(address))
                        TryDonwload = 0

                    Catch ex As Exception

                        TryDonwload += 1

                        MyApp.WriteProcLog(SF("!!! CONNECT ERROR !!! errore n:{0} - {1} - {2}", TryDonwload, ex.Message, Me.webClient.RequestUri.AbsolutePath), MyApp.Task.TaskType.NET_CONNECT)

                        Me.LastError.ResError = "!!! CONNECT ERROR !!!"
                        Me.LastError.Code = -1
                        Me.LastError.Message = ex.Message

                    End Try

                    Select Case Me.webClient.StatusCode

                        Case 500, 502, 503, 504

                            TryDonwload += 1

                            MyApp.WriteProcLog(SF("!!! SERVER ERROR !!! errore n:{0} - {1} - {2}", TryDonwload, Res, Me.webClient.StatusCode), MyApp.Task.TaskType.NET_API)

                            Me.LastError.ResError = Res
                            Me.LastError.Code = Me.webClient.StatusCode
                            Me.LastError.Message = Me.webClient.StatusCodeDescr

                    End Select

                    Select Case TryDonwload
                        Case 1
                            Threading.Thread.Sleep(2000)
                        Case 2
                            Threading.Thread.Sleep(10000)
                        Case 3
                            Threading.Thread.Sleep(20000)
                        Case 4
                            Threading.Thread.Sleep(60000)
                        Case Is > 4

                            If Me.LastError.Code < 0 Then
                                Net.ExceptionBase.ThrowException(New Net.ExceptionConnectionNetwork With {.ServiceError = Me.LastError})
                            Else
                                Net.ExceptionBase.ThrowException(New Net.ExceptionConnectionServer With {.ServiceError = Me.LastError})
                            End If

                    End Select


                Loop Until TryDonwload = 0

                webClient.postParams.Clear()
                webClient.Multipart = False

                If Me.webClient.StatusCode <> 200 Then

                    Me.LastError.ResError = Res

                    Try
                        Dim Err As ServiceErrors
                        Err = MyApp.JSONDeserializeObj(Of ServiceErrors)(Res)

                        If Err.errors.Count > 0 Then
                            Me.LastError.Code = Err.errors(0).code
                            Me.LastError.Message = Err.errors(0).message
                        Else
                            Me.LastError.Code = Me.webClient.StatusCode
                            Me.LastError.Message = Err.request
                        End If

                    Catch ex As Exception

                        Me.LastError.Code = Me.webClient.StatusCode
                        Me.LastError.Message = Res

                    End Try


                    Select Case Me.webClient.StatusCode

                        Case 404
                            'Not Found
                            Select Case Me.LastError.Code
                                Case 34, 144, 17
                                    '17 No user matches for specified terms.
                                    '34 Sorry, that page does not exist
                                    '144 No status found with that ID
                                    Net.ExceptionBase.ThrowException(New Net.ExceptionAPIResponse With {.ServiceError = Me.LastError})

                                Case 404
                                    Net.ExceptionBase.ThrowException(New Net.ExceptionAPIResponse With {.ServiceError = Me.LastError})

                            End Select


                        Case 429
                            'Too Many Requests
                            Net.ExceptionBase.ThrowException(New Net.ExceptionAPITooManyRequests With {.ServiceError = Me.LastError})
                        Case 401
                            'Unauthorized
                            Select Case Me.LastError.Code
                                Case 32, 135, 215, 89
                                    'Could not authenticate you
                                    'Could not authenticate you
                                    'Bad authentication data
                                    '89 Invalid or expired token
                                    Net.ExceptionBase.ThrowException(New Net.ExceptionAPIAccount With {.ServiceError = Me.LastError})

                                Case 401
                                    Net.ExceptionBase.ThrowException(New Net.ExceptionAPIResponse With {.ServiceError = Me.LastError})

                            End Select
                        Case 403
                            'Forbidden
                            Select Case Me.LastError.Code
                                Case 64
                                    'Your account is suspended and is not permitted to access this feature.
                                    Net.ExceptionBase.ThrowException(New Net.ExceptionAPIAccount With {.ServiceError = Me.LastError})
                                Case Else
                                    Net.ExceptionBase.ThrowException(New Net.ExceptionAPIResponse With {.ServiceError = Me.LastError})
                            End Select

                    End Select




                    'End Select

                    Net.ExceptionBase.ThrowException(New Net.ExceptionBase With {.ServiceError = Me.LastError})

                End If


                Return Res

            End Function

            Private Function Download_old(address As String) As String

                Me.LastError = New Net.ServiceError With {
                    .webClient = Me.webClient,
                    .Net = WebApi.Type.TWITTER,
                    .AutoProc = Me.AutoProc
                }

                Dim Res = ""
                Dim TryDonwload = 0
                Do
                    Try
                        Res = Encoding.UTF8.GetString(Me.webClient.DownloadData(address))
                        TryDonwload = 0

                    Catch ex As Exception

                        TryDonwload += 1

                        MyApp.WriteProcLog(SF("!!! CONNECT ERROR !!! errore n:{0} - {1} - {2}", TryDonwload, ex.Message, Me.webClient.RequestUri.AbsolutePath), MyApp.Task.TaskType.NET_CONNECT)

                        Select Case TryDonwload
                            Case 1
                                Threading.Thread.Sleep(2000)
                            Case 2
                                Threading.Thread.Sleep(10000)
                            Case 3
                                Threading.Thread.Sleep(20000)
                            Case 4
                                Threading.Thread.Sleep(60000)
                            Case Is > 4

                                Me.LastError.ResError = "!!! CONNECT ERROR !!!"
                                Me.LastError.Code = -1
                                Me.LastError.Message = ex.Message
                                Net.ExceptionBase.ThrowException(New Net.ExceptionConnectionNetwork With {.ServiceError = Me.LastError})

                        End Select

                    End Try

                    Select Case Me.webClient.StatusCode

                        Case 500, 502, 503, 504

                            TryDonwload += 1

                            MyApp.WriteProcLog(SF("!!! SERVER ERROR !!! errore n:{0} - {1} - {2}", TryDonwload, Res), MyApp.Task.TaskType.NET_API)


                            Select Case TryDonwload
                                Case 1
                                    Threading.Thread.Sleep(2000)
                                Case 2
                                    Threading.Thread.Sleep(10000)
                                Case 3
                                    Threading.Thread.Sleep(20000)
                                Case 4
                                    Threading.Thread.Sleep(60000)
                                Case Else

                                    Me.LastError.ResError = Res
                                    Me.LastError.Code = Me.webClient.StatusCode
                                    Me.LastError.Message = Me.webClient.StatusCodeDescr

                                    Net.ExceptionBase.ThrowException(New Net.ExceptionConnectionServer With {.ServiceError = Me.LastError})


                            End Select

                        Case Else
                            TryDonwload = 0

                    End Select



                Loop Until TryDonwload = 0


                webClient.postParams.Clear()
                webClient.Multipart = False

                If Me.webClient.StatusCode <> 200 Then

                    Me.LastError.ResError = Res

                    'Select Case Me.webClient.StatusCode
                    '    Case 500, 502, 503, 504
                    '        'Internal Server Error
                    '        'Bad Gateway
                    '        'Service Unavailable
                    '        'Gateway timeout

                    '        'Me.LastError = New Net.ServiceError With {.Message = Me.webClient.StatusCodeDescr, .Code = Me.webClient.StatusCode}
                    '        Me.LastError.Code = Me.webClient.StatusCode
                    '        Me.LastError.Message = Me.webClient.StatusCodeDescr

                    '        Net.ExceptionBase.ThrowException(New Net.ExceptionConnectionServer With {.ServiceError = Me.LastError})

                    '    Case Else

                    Try
                        Dim Err As ServiceErrors
                        Err = MyApp.JSONDeserializeObj(Of ServiceErrors)(Res)

                        If Err.errors.Count > 0 Then
                            Me.LastError.Code = Err.errors(0).code
                            Me.LastError.Message = Err.errors(0).message
                        Else
                            Me.LastError.Code = 401
                            Me.LastError.Message = Err.request
                        End If

                    Catch ex As Exception

                        Me.LastError.Code = Me.webClient.StatusCode
                        Me.LastError.Message = Res

                    End Try


                    Select Case Me.webClient.StatusCode

                        Case 404
                            'Not Found
                            Select Case Me.LastError.Code
                                Case 34
                                    'Sorry, that page does not exist
                                    Net.ExceptionBase.ThrowException(New Net.ExceptionAPIResponse With {.ServiceError = Me.LastError})

                            End Select


                        Case 429
                            'Too Many Requests
                            Net.ExceptionBase.ThrowException(New Net.ExceptionAPITooManyRequests With {.ServiceError = Me.LastError})
                        Case 401
                            'Unauthorized
                            Select Case Me.LastError.Code
                                Case 32, 135, 215, 89
                                    'Could not authenticate you
                                    'Could not authenticate you
                                    'Bad authentication data
                                    '89 Invalid or expired token
                                    Net.ExceptionBase.ThrowException(New Net.ExceptionAPIAccount With {.ServiceError = Me.LastError})

                                Case 401
                                    Net.ExceptionBase.ThrowException(New Net.ExceptionAPIResponse With {.ServiceError = Me.LastError})

                            End Select
                        Case 403
                            'Forbidden
                            Select Case Me.LastError.Code
                                Case 64
                                    'Your account is suspended and is not permitted to access this feature.
                                    Net.ExceptionBase.ThrowException(New Net.ExceptionAPIAccount With {.ServiceError = Me.LastError})
                                Case Else
                                    Net.ExceptionBase.ThrowException(New Net.ExceptionAPIResponse With {.ServiceError = Me.LastError})
                            End Select

                    End Select




                    'End Select

                    Net.ExceptionBase.ThrowException(New Net.ExceptionBase With {.ServiceError = Me.LastError})

                End If


                Return Res

            End Function

            'Private Sub ThrowException(ex As Net.ExceptionServiceBase)

            '    ex.webclient = Me.webClient
            '    ex.ServiceError = Me.LastError

            '    Dim WebError = SF("client:{0}, {1} - TWITTER API ERROR: {2}", Me.webClient.StatusCode, Me.webClient.StatusCodeDescr, Me.LastError.ResError)

            '    ex.Data.Add("ServiceResponse_Error", WebError)
            '    ex.Data.Add("ServiceResponse_URL", Me.webClient.ResponseUri.AbsolutePath)
            '    ex.Data.Add("ServiceResponse_oauthToken", Me.webClient.oAuth.oauthToken)

            '    UI.Page.WriteLog(WebError)

            '    Throw ex
            '    'Return ex

            'End Sub


#Region "USERS"

            Public Function GetUsersLookupPost(screen_name As String, user_id As String) As List(Of User)

                Dim Uri As New UriBuilder("https://api.twitter.com/1.1/users/lookup.json")

                'Dim queryString = HttpUtility.ParseQueryString("")
                Me.webClient.postParams.Add("include_entities", webClient.UrlEncode("false"))
                'queryString.Add("include_entities", "false")

                If screen_name <> "" Then
                    Me.webClient.postParams.Add("screen_name", webClient.UrlEncode(screen_name))
                    'queryString.Add("screen_name", screen_name)
                Else
                    Me.webClient.postParams.Add("user_id", webClient.UrlEncode(user_id))
                    'queryString.Add("user_id", user_id)
                End If
                'Uri.Query = queryString.ToString

                Dim Res = Me.Download(Uri.Uri.AbsoluteUri)
                Return MyApp.JSONDeserializeObj(Of List(Of User))(Res)


            End Function

            Public Function GetUsersLookup(screen_name As String, user_id As String) As List(Of User)

                Dim Uri As New UriBuilder("https://api.twitter.com/1.1/users/lookup.json")

                Dim queryString = HttpUtility.ParseQueryString("")
                'Me.webClient.postParams.Add("include_entities", webClient.UrlEncode("false"))
                queryString.Add("include_entities", "false")

                If screen_name <> "" Then
                    'Me.webClient.postParams.Add("screen_name", webClient.UrlEncode(screen_name))
                    queryString.Add("screen_name", screen_name)
                Else
                    'Me.webClient.postParams.Add("user_id", webClient.UrlEncode(user_id))
                    queryString.Add("user_id", user_id)
                End If
                Uri.Query = queryString.ToString

                Dim Res = Me.Download(Uri.Uri.AbsoluteUri)
                Return MyApp.JSONDeserializeObj(Of List(Of User))(Res)


            End Function

            Public Function GetUserShow(screen_name As String, user_id As String) As User

                Dim Uri As New UriBuilder("https://api.twitter.com/1.1/users/show.json")

                Dim queryString = HttpUtility.ParseQueryString("")

                If screen_name <> "" Then
                    queryString.Add("screen_name", screen_name)
                Else
                    queryString.Add("user_id", user_id)
                End If
                Uri.Query = queryString.ToString

                Dim Res = Me.webClient.DownloadString(Uri.Uri.AbsoluteUri)

                Return MyApp.JSONDeserializeObj(Of User)(Res)

            End Function

            Public Function GetUsersSuggestion(slug As String, Lang As String) As UserSuggestion

                Dim Uri As New UriBuilder(SF("https://api.twitter.com/1.1/users/suggestions/{0}.json", slug))

                Dim queryString = HttpUtility.ParseQueryString("")

                If Lang <> "" Then
                    queryString.Add("lang", Lang)
                End If
                Uri.Query = queryString.ToString

                Dim Res = Me.webClient.DownloadString(Uri.Uri.AbsoluteUri)

                Return MyApp.JSONDeserializeObj(Of UserSuggestion)(Res)

            End Function

            Public Function GetSuggestions(Lang As String) As List(Of UserSuggestion)

                Dim Uri As New UriBuilder(SF("https://api.twitter.com/1.1/users/suggestions.json"))

                Dim queryString = HttpUtility.ParseQueryString("")

                If Lang <> "" Then
                    queryString.Add("lang", Lang)
                End If
                Uri.Query = queryString.ToString

                Dim Res = Me.webClient.DownloadString(Uri.Uri.AbsoluteUri)

                Return MyApp.JSONDeserializeObj(Of List(Of UserSuggestion))(Res)

            End Function


#End Region

#Region "TIMELINES"

            '''  <summary>
            ''' Returns the 20 most recent mentions (tweets containing a users's @screen_name) for the authenticating user. The timeline returned is the equivalent of the one seen when you view your mentions on twitter.com. This method can only return up to 800 tweets.
            ''' </summary>
            Public Function GetMentions(since_id As String, max_id As String) As List(Of Tweet)

                Dim Uri As New UriBuilder("https://api.twitter.com/1.1/statuses/mentions_timeline.json")

                Dim queryString = HttpUtility.ParseQueryString("")
                queryString.Add("include_entities", "false")
                queryString.Add("count", "200")
                queryString.Add("contributor_details", "false")

                If since_id <> "" Then
                    queryString.Add("since_id", since_id)
                End If

                If max_id <> "" Then
                    queryString.Add("max_id", max_id)
                End If

                Uri.Query = queryString.ToString

                Dim Res = Me.Download(Uri.Uri.AbsoluteUri)
                Return MyApp.JSONDeserializeObj(Of List(Of Tweet))(Res)

            End Function

            ''' <summary>
            ''' Returns a collection of the most recent Tweets posted by the user indicated by the screen_name or user_id parameters. User timelines belonging to protected users may only be requested when the authenticated user either "owns" the timeline or is an approved follower of the owner.
            ''' </summary>
            Public Function GetTweets(screen_name As String, user_id As String, since_id As String, max_id As String) As List(Of Tweet)

                Dim Uri As New UriBuilder("https://api.twitter.com/1.1/statuses/user_timeline.json")

                Dim queryString = HttpUtility.ParseQueryString("")
                queryString.Add("contributor_details", "false")
                queryString.Add("count", "200")
                queryString.Add("include_rts", "true")

                If screen_name <> "" Then
                    queryString.Add("screen_name", screen_name)
                Else
                    queryString.Add("user_id", user_id)
                End If

                If since_id <> "" Then
                    queryString.Add("since_id", since_id)
                End If

                If max_id <> "" Then
                    queryString.Add("max_id", max_id)
                End If

                Uri.Query = queryString.ToString

                Dim Res = Me.Download(Uri.Uri.AbsoluteUri)
                Return MyApp.JSONDeserializeObj(Of List(Of Tweet))(Res)

            End Function
            Public Function GetTweets(screen_name As String, user_id As String) As List(Of Tweet)

                Return GetTweets(screen_name, user_id, "", "")

            End Function

            ''' <summary>
            ''' Returns a collection of the most recent Tweets and retweets posted by the authenticating user and the users they follow. The home timeline is central to how most users interact with the Twitter service. Up to 800 Tweets are obtainable on the home timeline.
            ''' Different from getTweets as this return also tweets from people followed by current user authenticated
            ''' </summary>
            ''' <returns>List(Of Tweet)</returns>
            ''' <remarks></remarks>
            Public Function GetHomeTweets() As List(Of Tweet)

                Dim Uri As New UriBuilder("https://api.twitter.com/1.1/statuses/home_timeline.json")

                Dim queryString = HttpUtility.ParseQueryString("")
                queryString.Add("include_entities", "true")
                queryString.Add("count", "800")
                Uri.Query = queryString.ToString

                Dim Res As String = ""
                Res = Me.webClient.DownloadString(Uri.Uri.AbsoluteUri)

                Return MyApp.JSONDeserializeObj(Of List(Of Tweet))(Res)

            End Function

            ''' <summary>
            ''' Returns the most recent tweets authored by the authenticating user that have recently been retweeted by others. This timeline is a subset of the user's GET statuses/user_timeline.
            ''' </summary>
            Public Function GetMyTweetsRetwitted(since_id As String, max_id As String) As List(Of Tweet)

                Dim Uri As New UriBuilder("https://api.twitter.com/1.1/statuses/retweets_of_me.json")

                Dim queryString = HttpUtility.ParseQueryString("")
                queryString.Add("include_entities", "false")
                queryString.Add("count", "100")
                queryString.Add("include_user_entities", "false")

                If since_id <> "" Then
                    queryString.Add("since_id", since_id)
                End If

                If max_id <> "" Then
                    queryString.Add("max_id", max_id)
                End If

                Uri.Query = queryString.ToString

                Dim Res = Me.Download(Uri.Uri.AbsoluteUri)
                Return MyApp.JSONDeserializeObj(Of List(Of Tweet))(Res)


            End Function
#End Region

#Region "TWEETS"

            ''' <summary>
            ''' Returns first 100 retweets of a given tweet id.
            ''' </summary>
            ''' <param name="tweetId"></param>
            ''' <returns> List(Of Tweet)</returns>
            ''' <remarks></remarks>
            Public Function GetRetweetsOfTweet(tweetId As String) As List(Of Tweet)

                Dim Uri As New UriBuilder(SF("https://api.twitter.com/1.1/statuses/retweets/{0}.json", tweetId))

                Dim queryString = HttpUtility.ParseQueryString("")
                queryString.Add("count", "100")

                Uri.Query = queryString.ToString

                Dim Res = Me.Download(Uri.Uri.AbsoluteUri)
                Return MyApp.JSONDeserializeObj(Of List(Of Tweet))(Res)

            End Function

            ''' <summary>
            ''' Returns a single Tweet, specified by the id parameter. The Tweet's author will also be embedded within the tweet.
            ''' </summary>
            ''' <param name="tweetId"></param>
            ''' <returns>Tweet</returns>
            ''' <remarks></remarks>
            Public Function GetTweet(tweetId As String) As Tweet

                Dim Uri As New UriBuilder("https://api.twitter.com/1.1/statuses/show.json")
                Dim queryString = HttpUtility.ParseQueryString("")
                queryString.Add("id", tweetId)
                queryString.Add("include_entities", "false")

                Uri.Query = queryString.ToString

                Dim Res = Me.Download(Uri.Uri.AbsoluteUri)

                Return MyApp.JSONDeserializeObj(Of Tweet)(Res)

            End Function

            ''' <summary>
            ''' Post a tweet by the current user authorizated
            ''' </summary>
            ''' <param name="status"></param>
            ''' <remarks></remarks>
            Public Function PostTweetMedia(status As String, File As String) As Tweet


                Dim s As New IO.FileStream(File, IO.FileMode.Open)
                Dim buff(s.Length - 1) As Byte
                s.Read(buff, 0, s.Length)
                Dim img = Convert.ToBase64String(buff)
                s.Close()

                Return PostTweetMediaBase64(status, img)
                'Dim Res As String = ""
                'Res = Me.webClient.DownloadString(Uri.Uri.AbsoluteUri)

            End Function
            Public Function PostTweetMediaBase64(status As String, data64 As String) As Tweet

                Dim Uri As New UriBuilder("https://api.twitter.com/1.1/statuses/update_with_media.json")

                Me.webClient.Multipart = True
                Me.webClient.postParams.Add("status", status)
                Me.webClient.postParams.Add("media[]", data64)
                'Me.webClient.postParams.Add("status", status)

                Dim Res = Me.Download(Uri.Uri.AbsoluteUri)

                Return MyApp.JSONDeserializeObj(Of Tweet)(Res)
                'Dim Res As String = ""
                'Res = Me.webClient.DownloadString(Uri.Uri.AbsoluteUri)

            End Function
            Public Function PostTweet(status As String) As Tweet


                Return Me.PostTweet(status, "", "", "")

            End Function
            Public Function PostTweet(status As String, in_reply_to_status_id As String, lat As String, lng As String) As Tweet

                Dim Uri As New UriBuilder("https://api.twitter.com/1.1/statuses/update.json")



                Me.webClient.postParams.Add("status", Net.WebClient.UrlEncode(status))
                'Me.webClient.postParams.Add("status", status)

                If in_reply_to_status_id <> "" Then
                    Me.webClient.postParams.Add("in_reply_to_status_id", in_reply_to_status_id)
                End If

                If lat <> "" Then
                    Me.webClient.postParams.Add("lat", lat)
                End If

                If lng <> "" Then
                    Me.webClient.postParams.Add("long", lng)
                End If

                Dim Res = Me.Download(Uri.Uri.AbsoluteUri)

                Return MyApp.JSONDeserializeObj(Of Tweet)(Res)
                'Dim Res As String = ""
                'Res = Me.webClient.DownloadString(Uri.Uri.AbsoluteUri)

            End Function


            Public Function PostReTweet(id As String) As Tweet

                Dim Uri As New UriBuilder(SF("https://api.twitter.com/1.1/statuses/retweet/{0}.json", id))

                Me.webClient.postParams.Add("trim_user", "true")


                Dim Res = Me.Download(Uri.Uri.AbsoluteUri)
                Return MyApp.JSONDeserializeObj(Of Tweet)(Res)

            End Function
            Public Function PostStatusesDestroy(id As String) As Tweet

                Dim Uri As New UriBuilder(SF("https://api.twitter.com/1.1/statuses/destroy/{0}.json", id))

                Me.webClient.postParams.Add("trim_user", "true")


                Dim Res = Me.Download(Uri.Uri.AbsoluteUri)
                Return MyApp.JSONDeserializeObj(Of Tweet)(Res)

            End Function
#End Region

#Region "SEARCH"
            Public Function GetSearch(q As String, since_id As String, max_id As String, result_type As String, geocode As String, lang As String) As SearchResults

                Dim Uri As New UriBuilder("https://api.twitter.com/1.1/search/tweets.json")

                Dim queryString = HttpUtility.ParseQueryString("")

                'queryString.Add("q", HttpUtility.UrlEncode(q))
                'queryString.Add("q", q)
                queryString.Add("q", webClient.UrlEncode(q))

                queryString.Add("include_entities", "false")
                queryString.Add("count", "100")
                'queryString.Add("since_id", sinceId.ToString)
                If result_type <> "" Then
                    queryString.Add("result_type", result_type) ' mixed, recent, popular
                End If

                If geocode <> "" Then
                    queryString.Add("geocode", geocode) ' 37.781157,-122.398720,20km
                End If

                If lang <> "" Then
                    queryString.Add("lang", lang)
                End If

                If since_id <> "" Then
                    queryString.Add("since_id", since_id)
                End If

                If max_id <> "" Then
                    queryString.Add("max_id", max_id)
                End If

                Uri.Query = queryString.ToString '& "&q=" & webClient.UrlEncode(q)

                Dim Res = Me.Download(Uri.Uri.AbsoluteUri)

                Return MyApp.JSONDeserializeObj(Of SearchResults)(Res)


            End Function
            Public Function GetSearch(q As String, since_id As String, max_id As String) As SearchResults

                Return GetSearch(q, since_id, max_id, "", "", "")

            End Function

            Public Sub InitStreamSearch(q As String)

                Dim Uri As New UriBuilder("https://stream.twitter.com/1.1/statuses/filter.json")

                Me.webClient.postParams.Add("track", Net.WebClient.UrlEncode(q))

                Me.DownloadInit(Uri.Uri.AbsoluteUri)


            End Sub
            Public Function GetStreamSearch() As String


                Dim Res = Me.DownloadLine

                'Dim Res = Me.Download(Uri.Uri.AbsoluteUri)
                Return Res
                'Return MyApp.JSONDeserialize(Of List(Of Tweet))(Res)

            End Function

            Public Function GetStreamSearchSample() As String

                Dim Uri As New UriBuilder("https://stream.twitter.com/1.1/statuses/sample.json")

                'Me.webClient.postParams.Add("status", status)

                Me.DownloadInit(Uri.Uri.AbsoluteUri)

                Dim Res = Me.DownloadLine

                'Dim Res = Me.Download(Uri.Uri.AbsoluteUri)
                Return Res
                'Return MyApp.JSONDeserialize(Of List(Of Tweet))(Res)

            End Function
#End Region

#Region "DIRECT MESSAGES"

            Public Function GetDirectMessage(since_id As String, max_id As String) As List(Of Tweet)

                Dim Uri As New UriBuilder("https://api.twitter.com/1.1/direct_messages.json")

                Dim queryString = HttpUtility.ParseQueryString("")
                queryString.Add("include_entities", "false")
                queryString.Add("count", "200")
                queryString.Add("skip_status", "true")

                If since_id <> "" Then
                    queryString.Add("since_id", since_id)
                End If

                If max_id <> "" Then
                    queryString.Add("max_id", max_id)
                End If

                Uri.Query = queryString.ToString

                Dim Res = Me.Download(Uri.Uri.AbsoluteUri)
                Return MyApp.JSONDeserializeObj(Of List(Of Tweet))(Res)

            End Function
            Public Function GetDirectMessageSent(since_id As String, max_id As String) As List(Of Tweet)

                Dim Uri As New UriBuilder("https://api.twitter.com/1.1/direct_messages/sent.json")

                Dim queryString = HttpUtility.ParseQueryString("")
                queryString.Add("include_entities", "false")
                queryString.Add("count", "200")
                queryString.Add("skip_status", "true")

                If since_id <> "" Then
                    queryString.Add("since_id", since_id)
                End If

                If max_id <> "" Then
                    queryString.Add("max_id", max_id)
                End If

                Uri.Query = queryString.ToString

                Dim Res = Me.Download(Uri.Uri.AbsoluteUri)
                Return MyApp.JSONDeserializeObj(Of List(Of Tweet))(Res)

            End Function
            Public Function PostDirectMessage(screen_name As String, user_id As String, status As String) As Tweet

                Dim Uri As New UriBuilder("https://api.twitter.com/1.1/direct_messages/new.json")

                Me.webClient.postParams.Add("text", webClient.UrlEncode(status))

                If screen_name <> "" Then
                    Me.webClient.postParams.Add("screen_name", webClient.UrlEncode(screen_name))
                Else
                    Me.webClient.postParams.Add("user_id ", webClient.UrlEncode(user_id))
                End If


                Dim Res = Me.Download(Uri.Uri.AbsoluteUri)

                Return MyApp.JSONDeserializeObj(Of Tweet)(Res)


            End Function
#End Region

#Region "FRIENDS & FOLLOWERS"
            Public Function GetFollowers(screen_name As String, user_id As String, cursor As String) As CollectionsId

                Return GetFollowers(screen_name, user_id, cursor, 0)

            End Function

            Public Function GetFollowers(screen_name As String, user_id As String, cursor As String, count As Integer) As CollectionsId

                Dim Uri As New UriBuilder("https://api.twitter.com/1.1/followers/ids.json")

                Dim queryString = HttpUtility.ParseQueryString("")
                queryString.Add("cursor", cursor)

                If count <> 0 Then
                    queryString.Add("count", count)
                End If

                If screen_name <> "" Then
                    queryString.Add("screen_name", screen_name)
                Else
                    queryString.Add("user_id", user_id)
                End If
                Uri.Query = queryString.ToString

                Dim Res = Me.Download(Uri.Uri.AbsoluteUri)
                Return MyApp.JSONDeserializeObj(Of CollectionsId)(Res)

                'Dim Res = Me.webClient.DownloadString(Uri.Uri.AbsoluteUri)

                'Return MyApp.JSONDeserialize(Of Followers)(Res)

            End Function

            Public Function GetFriends(screen_name As String, user_id As String, cursor As String) As CollectionsId

                Dim Uri As New UriBuilder("https://api.twitter.com/1.1/friends/ids.json")

                Dim queryString = HttpUtility.ParseQueryString("")
                queryString.Add("cursor", cursor)

                If screen_name <> "" Then
                    queryString.Add("screen_name", screen_name)
                Else
                    queryString.Add("user_id", user_id)
                End If
                Uri.Query = queryString.ToString

                Dim Res = Me.Download(Uri.Uri.AbsoluteUri)
                Return MyApp.JSONDeserializeObj(Of CollectionsId)(Res)

                'Dim Res = Me.webClient.DownloadString(Uri.Uri.AbsoluteUri)

                'Return MyApp.JSONDeserialize(Of Followers)(Res)

            End Function

            Public Function PostFriendshipsCreate(screen_name As String, user_id As String) As User

                Dim Uri As New UriBuilder("https://api.twitter.com/1.1/friendships/create.json")

                If screen_name <> "" Then
                    Me.webClient.postParams.Add("screen_name", screen_name)
                Else
                    Me.webClient.postParams.Add("user_id", user_id)
                End If

                Me.webClient.postParams.Add("follow", "false")

                Dim Res = Me.Download(Uri.Uri.AbsoluteUri)

                Return MyApp.JSONDeserializeObj(Of User)(Res)
                'Dim Res As String = ""
                'Res 
            End Function
            Public Function PostFriendshipsDestroy(screen_name As String, user_id As String) As User

                Dim Uri As New UriBuilder("https://api.twitter.com/1.1/friendships/destroy.json")

                If screen_name <> "" Then
                    Me.webClient.postParams.Add("screen_name", screen_name)
                Else
                    Me.webClient.postParams.Add("user_id", user_id)
                End If

                Dim Res = Me.Download(Uri.Uri.AbsoluteUri)
                Return MyApp.JSONDeserializeObj(Of User)(Res)

            End Function

            Public Function GetFriendshipsLookup(screen_name As String, user_id As String) As List(Of Relationship)

                Dim Uri As New UriBuilder("https://api.twitter.com/1.1/friendships/lookup.json")

                Dim queryString = HttpUtility.ParseQueryString("")

                If screen_name <> "" Then
                    queryString.Add("screen_name", screen_name)
                Else
                    queryString.Add("user_id", user_id)
                End If
                Uri.Query = queryString.ToString

                Dim Res = Me.Download(Uri.Uri.AbsoluteUri)
                Return MyApp.JSONDeserializeObj(Of List(Of Relationship))(Res)

            End Function
            '

            'Public Function GetFriendshipsLookup(screen_name As String, user_id As String) As List(Of User)

            '    Dim Uri As New UriBuilder("https://api.twitter.com/1.1/friendships/lookup.json")

            '    Dim queryString = HttpUtility.ParseQueryString("")
            '    'queryString.Add("include_entities", "false")

            '    If screen_name <> "" Then
            '        queryString.Add("screen_name", screen_name)
            '    Else
            '        queryString.Add("user_id", user_id)
            '    End If
            '    Uri.Query = queryString.ToString

            '    Dim Res = Me.webClient.DownloadString(Uri.Uri.AbsoluteUri)

            '    Return MyApp.JSONDeserialize(Of List(Of User))(Res)

            'End Function


#End Region

#Region "PLACES & GEO"

            Public Function GetGeoSearch(q As String) As GeoResponse

                Dim Uri As New UriBuilder(SF("https://api.twitter.com/1.1/geo/search.json"))

                Dim queryString = HttpUtility.ParseQueryString("")
                queryString.Add("query", webClient.UrlEncode(q))
                queryString.Add("max_results", 100)

                Uri.Query = queryString.ToString

                Dim Res = Me.Download(Uri.Uri.AbsoluteUri)
                Return MyApp.JSONDeserializeObj(Of GeoResponse)(Res)

            End Function

#End Region

#Region "LISTS"

            Public Function PostListsCreate(name As String, mode As String, description As String) As List

                Dim Uri As New UriBuilder("https://api.twitter.com/1.1/lists/create.json")

                Me.webClient.postParams.Add("name", name)
                Me.webClient.postParams.Add("mode", mode)
                Me.webClient.postParams.Add("description", description)

                Me.webClient.postParams.Add("follow", "false")

                Dim Res = Me.Download(Uri.Uri.AbsoluteUri)
                Return MyApp.JSONDeserializeObj(Of List)(Res)
                'Dim Res As String = ""
                'Res 
            End Function

            Public Function PostListsDestroy(list_id As String) As List

                Dim Uri As New UriBuilder("https://api.twitter.com/1.1/lists/destroy.json")

                Me.webClient.postParams.Add("list_id", list_id)

                Dim Res = Me.Download(Uri.Uri.AbsoluteUri)
                Return MyApp.JSONDeserializeObj(Of List)(Res)
                'Dim Res As String = ""
                'Res 
            End Function

            Public Function PostListsMembersCreateAll(list_id As String, user_id As String) As List

                Dim Uri As New UriBuilder("https://api.twitter.com/1.1/lists/members/create_all.json")

                Me.webClient.postParams.Add("list_id", list_id)
                Me.webClient.postParams.Add("user_id", Net.WebClient.UrlEncode(user_id))

                Dim Res = Me.Download(Uri.Uri.AbsoluteUri)
                Return MyApp.JSONDeserializeObj(Of List)(Res)
                'Dim Res As String = ""
                'Res 
            End Function

            Public Function GetListsOwnerships(screen_name As String, user_id As String, cursor As String) As collectionsList

                Dim Uri As New UriBuilder("https://api.twitter.com/1.1/lists/ownerships.json")

                Dim queryString = HttpUtility.ParseQueryString("")
                queryString.Add("cursor", cursor)

                If screen_name <> "" Then
                    queryString.Add("screen_name", screen_name)
                Else
                    queryString.Add("user_id", user_id)
                End If
                Uri.Query = queryString.ToString

                Dim Res = Me.Download(Uri.Uri.AbsoluteUri)
                Return MyApp.JSONDeserializeObj(Of collectionsList)(Res)


            End Function

            Public Function GetListsMembers(List_id As String, cursor As String) As CollectionsUser

                Dim Uri As New UriBuilder("https://api.twitter.com/1.1/lists/members.json")

                Dim queryString = HttpUtility.ParseQueryString("")
                queryString.Add("list_id", List_id)
                queryString.Add("cursor", cursor)
                queryString.Add("count", 5000)
                queryString.Add("include_entities", "false")
                queryString.Add("skip_status", "false")


                Uri.Query = queryString.ToString

                Dim Res = Me.Download(Uri.Uri.AbsoluteUri)
                Return MyApp.JSONDeserializeObj(Of CollectionsUser)(Res)


            End Function

#End Region

#Region "FAVORITES"

            Public Function GetFavorites(screen_name As String, user_id As String, since_id As String, max_id As String) As List(Of Tweet)

                Dim Uri As New UriBuilder("https://api.twitter.com/1.1/favorites/list.json")

                Dim queryString = HttpUtility.ParseQueryString("")
                queryString.Add("count", "200")
                queryString.Add("include_entities", "false")

                If screen_name <> "" Then
                    queryString.Add("screen_name", screen_name)
                Else
                    queryString.Add("user_id", user_id)
                End If

                If since_id <> "" Then
                    queryString.Add("since_id", since_id)
                End If

                If max_id <> "" Then
                    queryString.Add("max_id", max_id)
                End If

                Uri.Query = queryString.ToString

                Dim Res = Me.Download(Uri.Uri.AbsoluteUri)
                Return MyApp.JSONDeserializeObj(Of List(Of Tweet))(Res)

            End Function

            Public Function PostFavoritesCreate(id As String) As Tweet

                Dim Uri As New UriBuilder("https://api.twitter.com/1.1/favorites/create.json")

                Me.webClient.postParams.Add("id", id)

                Dim Res = Me.Download(Uri.Uri.AbsoluteUri)

                Return MyApp.JSONDeserializeObj(Of Tweet)(Res)
                'Dim Res As String = ""
                'Res 
            End Function

            Public Function PostFavoritesDestroy(id As String) As Tweet

                Dim Uri As New UriBuilder("https://api.twitter.com/1.1/favorites/destroy.json")

                Me.webClient.postParams.Add("id", id)

                Dim Res = Me.Download(Uri.Uri.AbsoluteUri)

                Return MyApp.JSONDeserializeObj(Of Tweet)(Res)
                'Dim Res As String = ""
                'Res 
            End Function

#End Region


            Public Class ServiceErrors

                Public Property errors As New List(Of ServiceError)
                Public Property request As String
                Public Property [error] As String

            End Class

            Public Class ServiceError

                Public Property message As String
                Public Property code As Integer

            End Class

        End Class




        Public Class User

            Public Property id_str As String
            Public Property name As String
            Public Property profile_image_url As String
            Public Property location As String
            Public Property followers_count As Integer
            Public Property screen_name As String
            Public Property description As String
            Public Property url As String
            Public Property statuses_count As Integer
            Public Property friends_count As Integer
            Public Property lang As String
            Public Property [protected] As Boolean
            Public Property verified As Boolean
            Public Property status As Tweet
            Public Property listed_count As Integer


            Private _created_at As Date
            Public Property created_at() As String
                Get
                    Return _created_at
                End Get
                Set(ByVal value As String)
                    _created_at = DateTime.ParseExact(value, "ddd MMM dd HH:mm:ss zzzz yyyy", Globalization.CultureInfo.InvariantCulture).ToUniversalTime
                End Set
            End Property

            Private _utc_offset As Integer
            Public Property utc_offset() As String
                Get
                    Return _utc_offset
                End Get
                Set(ByVal value As String)
                    _utc_offset = value
                End Set
            End Property




        End Class

        Public Class UserSuggestion

            Public Property name As String
            Public Property slug As String
            Public Property size As Integer
            Public Property users As List(Of User)

        End Class


        Public Class Entities

            Public Property urls() As List(Of EntityUrl)
            Public Property hashtags() As List(Of EntityHashtag)
            Public Property media() As List(Of EntityMedia)

        End Class

        Public Class EntityUrl

            Public Property url() As String
            Public Property expanded_url() As String

        End Class

        Public Class EntityHashtag

            Public Property text() As String
            Public Property indices() As List(Of Integer)

        End Class

        Public Class EntityMedia

            Public Property media_url As String
            Public Property display_url As String
            'Public Property source_status_id_str As String

        End Class

        Public Class Tweet

            Public Property id_str As String
            Public Property text As String

            Private _created_at As Date
            Public Property created_at() As String
                Get
                    Return _created_at
                End Get
                Set(ByVal value As String)
                    _created_at = DateTime.ParseExact(value, "ddd MMM dd HH:mm:ss zzzz yyyy", Globalization.CultureInfo.InvariantCulture).ToUniversalTime
                End Set
            End Property

            Public Property entities As Entities
            Public Property retweet_count As Integer
            Public Property retweeted_status As Tweet
            Public Property retweeted As Boolean
            Public Property favorited As Boolean

            Public Property user As User

            Public Property in_reply_to_status_id_str As String
            Public Property in_reply_to_user_id_str As String

            'direct message
            Public Property recipient_id As String
            Public Property recipient_screen_name As String

            Public Property sender_id As String
            Public Property sender_screen_name As String


        End Class

        Public Class CollectionsId

            Public Property ids As List(Of String)
            Public Property next_cursor_str As String
            Public Property previous_cursor_str As String

        End Class
        Public Class CollectionsList

            Public Property lists As List(Of List)
            Public Property next_cursor_str As String
            Public Property previous_cursor_str As String

        End Class


        Public Class CollectionsUser

            Public Property users As List(Of User)
            Public Property next_cursor_str As String
            Public Property previous_cursor_str As String

        End Class
        Public Class List

            Public Property id_str As String
            Public Property name As String
            Public Property description As String

            Public Property member_count As Integer
            Public Property mode As String

            Private _created_at As Date
            Public Property created_at() As String
                Get
                    Return _created_at
                End Get
                Set(ByVal value As String)
                    _created_at = DateTime.ParseExact(value, "ddd MMM dd HH:mm:ss zzzz yyyy", Globalization.CultureInfo.InvariantCulture).ToUniversalTime
                End Set
            End Property


        End Class

        Public Class SearchResults

            Public Property statuses As List(Of Tweet)

        End Class

        Public Class GeoResponse

            Public Property result As GeoResult

        End Class

        Public Class GeoResult

            Public Property places As List(Of Place)

        End Class


        Public Class Place

            Public Property country As String
            Public Property country_code As String
            Public Property full_name As String
            Public Property id As String
            Public Property name As String
            Public Property place_type As String
            Public Property url As String

            Public Property contained_within As List(Of Place)

            Public Property bounding_box As BoundingBox

        End Class
        Public Class BoundingBox

            Public Property coordinates As List(Of List(Of List(Of Double)))
            Public Property type As String

        End Class

        Public Class Relationship

            Public Property name As String
            Public Property id_str As String
            Public Property connections As List(Of String)
            Public Property screen_name As String

        End Class

    End Namespace


    Namespace OpenstreetMap
        'http://nominatim.openstreetmap.org/search?q=modena&format=json&addressdetails=1

        'http://open.mapquestapi.com/nominatim/v1/search.php?format=json&q=modena&addressdetails=1

        Public Class OpenstreetMapClient

            Private webClient As WebApi.Net.WebClient

            Public Property LastError As Net.ServiceError

            Public Sub New()

                Me.webClient = New WebApi.Net.WebClient


            End Sub

            Private Function Download(address As String) As String

                Me.LastError = Nothing

                Dim Res = Me.webClient.DownloadString(address)


                webClient.postParams.Clear()

                If Me.webClient.StatusCode <> 200 Then

                    Me.LastError = New Net.ServiceError With {
                        .webClient = Me.webClient,
                        .ResError = Res,
                        .Net = WebApi.Type.OPENSTREETMAP
                    }

                    Select Case Me.webClient.StatusCode
                        Case 500, 502, 503, 504
                            'Internal Server Error
                            'Bad Gateway
                            'Service Unavailable
                            'Gateway timeout

                            Me.LastError.Code = Me.webClient.StatusCode
                            Me.LastError.Message = Me.webClient.StatusCodeDescr

                            Net.ExceptionBase.ThrowException(New Net.ExceptionConnectionServer With {.ServiceError = Me.LastError})

                        Case Else

                            'Dim Err = MyApp.JSONDeserialize(Of ServiceErrors)(Res)

                            'If Err.errors.Count > 0 Then
                            '    Me.LastError = New Net.ServiceError With {.code = Err.errors(0).code, .message = Err.errors(0).message}
                            'Else
                            '    Me.LastError = New Net.ServiceError With {.message = Err.request, .code = 401}
                            'End If

                            Net.ExceptionBase.ThrowException(New Net.ExceptionAPIResponse With {.ServiceError = Me.LastError})


                    End Select

                    Net.ExceptionBase.ThrowException(New Net.ExceptionBase With {.ServiceError = Me.LastError})

                End If


                Return Res

            End Function
            'Private Sub ThrowException(ex As Net.ExceptionServiceBase)

            '    ex.webclient = Me.webClient
            '    ex.ServiceError = Me.LastError

            '    Throw ex
            '    'Return ex

            'End Sub

            Public Function GetGeoSearch(q As String) As List(Of Place)

                Dim Uri As New UriBuilder(SF("http://open.mapquestapi.com/nominatim/v1/search.php"))

                Dim queryString = HttpUtility.ParseQueryString("")
                queryString.Add("format", "json")
                queryString.Add("addressdetails", "1")
                queryString.Add("limit", "1")
                'queryString.Add("q", q)

                Uri.Query = queryString.ToString

                Dim Res = Me.Download(Uri.Uri.AbsoluteUri & "&q=" & q)
                Return MyApp.JSONDeserializeObj(Of List(Of Place))(Res)

            End Function

        End Class


        Public Class Place

            Public Property place_id As String
            Public Property display_name As String

            Public Property type As String

            Public Property boundingbox As List(Of Double)

            Public Property address As PlaceAddress

            Public Property lat As Double
            Public Property lon As Double

            Public Property importance As Double



        End Class
        Public Class PlaceAddress
            Public Property locality As String
            Public Property city As String
            Public Property county As String
            Public Property state As String
            Public Property country As String
            Public Property country_code As String

        End Class
        'Public Class BoundingBox

        '    Public Property coordinates As List(Of List(Of List(Of Double)))
        '    Public Property type As String

        'End Class


    End Namespace

    Namespace FeedRss

        Public Class FeedRssClient

            Private webClient As WebApi.Net.WebClient

            Public Property LastError As Net.ServiceError


            Public Property lastmod As Date
            Public Property etag As String

            Public Sub New()

                Me.webClient = New WebApi.Net.WebClient


            End Sub

            Private Function Download(address As String) As String

                Me.LastError = Nothing

                Dim Res = Me.webClient.DownloadString(address)


                webClient.postParams.Clear()

                If Me.webClient.StatusCode <> 200 Then

                    Me.LastError = New Net.ServiceError With {
                        .webClient = Me.webClient,
                        .ResError = Res,
                        .Net = WebApi.Type.RSS
                    }


                    Select Case Me.webClient.StatusCode
                        Case 500, 502, 503, 504

                            Me.LastError.Code = Me.webClient.StatusCode
                            Me.LastError.Message = Me.webClient.StatusCodeDescr

                            Net.ExceptionBase.ThrowException(New Net.ExceptionConnectionServer With {.ServiceError = Me.LastError})

                        Case Else

                            Net.ExceptionBase.ThrowException(New Net.ExceptionAPIResponse With {.ServiceError = Me.LastError})

                    End Select

                    Net.ExceptionBase.ThrowException(New Net.ExceptionBase With {.ServiceError = Me.LastError})

                End If


                Return Res

            End Function
            'Private Sub ThrowException(ex As Net.ExceptionServiceBase)

            '    ex.webclient = Me.webClient
            '    ex.ServiceError = Me.LastError

            '    Throw ex
            '    'Return ex

            'End Sub

            Public Function GetFeed(q As String) As ServiceModel.Syndication.SyndicationFeed


                Me.webClient.LastModified = Me.lastmod
                Me.webClient.ETag = Me.etag


                Dim xml = Me.webClient.DownloadString(q)

                If Me.webClient.StatusCode = 304 Then

                    Return Nothing

                Else

                    Dim rr As New IO.StringReader(xml)
                    Dim reader As System.Xml.XmlReader = System.Xml.XmlReader.Create(rr)
                    Dim feed As ServiceModel.Syndication.SyndicationFeed = ServiceModel.Syndication.SyndicationFeed.Load(reader)

                    Me.lastmod = Me.webClient.LastModified
                    Me.etag = Me.webClient.ETag

                    Return feed

                End If

            End Function

        End Class



    End Namespace

End Namespace
